﻿using ICSharpCode.SharpZipLib.Core;
using ICSharpCode.SharpZipLib.Zip;
using Microsoft.Graph;
using Microsoft.Identity.Client;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using TabbedTemplate.Confiddential;
using TabbedTemplate.Models;
using TabbedTemplate.Util;

namespace TabbedTemplate.Services
{
    /// <summary>
    /// OneDrive收藏存储。
    /// </summary>
    public class OneDriveFavoriteStorage : IRemoteFavoriteStorage
    {
        /******** 公开变量 ********/

        /******** 私有变量 ********/

        /// <summary>
        /// 警告服务。
        /// </summary>
        private IAlertService _alertService;

        private IStoreStorage _storeStorage;
        /// <summary>
        /// OneDrive服务器。
        /// </summary>
        private const string Server = "OneDrive服务器";

        /// <summary>
        /// Microsoft Graph服务范围。
        /// </summary>
        private string[] _scopes = OneDriveOAuthSettings.Scopes.Split(' ');

        /// <summary>
        /// Microsoft Graph客户端验证工具。
        /// </summary>
        private IPublicClientApplication _pca;

        /// <summary>
        /// Microsoft Graph客户端。
        /// </summary>
        private GraphServiceClient _graphClient;

        /******** 继承方法 ********/

        /// <summary>
        /// 状态。
        /// </summary>
        public string Status
        {
            get => _status;
            set
            {
                _status = value;
                StatusChanged?.Invoke(this, EventArgs.Empty);
            }
        }

        private string _status;

        /// <summary>
        /// 状态改变事件。
        /// </summary>
        public event EventHandler StatusChanged;

       
        /// <summary>
        /// 获得StoreStorage。 
        /// </summary>
        public async Task<IList<Store>> GetStoresAsync()
        {
            var rootChildren = await _graphClient.Me.Drive.Root.Children
                .Request().GetAsync();

            if (!rootChildren.Any(p => p.Name == "TabbedTemplateStoreStorage.zip"))
            {
                return new List<Store>();
            }
            var fileStream = await _graphClient.Me.Drive.Root
                .ItemWithPath("/TabbedTemplateStoreStorage.zip").Content.Request().GetAsync();
            ZipInputStream zipStream = new ZipInputStream(fileStream);
            ZipEntry zipEntry = zipStream.GetNextEntry();

            if (zipEntry == null)
            {
                return new List<Store>();
            }
            byte[] buffer = new byte[1024];
            var jsonStream = new MemoryStream();
            StreamUtils.Copy(zipStream, jsonStream, buffer);
            zipStream.Close();
            fileStream.Close();

            jsonStream.Position = 0;
            var jsonReader = new StreamReader(jsonStream);
            var storeList =
                JsonConvert.DeserializeObject<IList<Store>>(
                    await jsonReader.ReadToEndAsync());
            jsonReader.Close();
            jsonStream.Close();

            return storeList?.ToList() ?? new List<Store>();
        }

        /// <summary>
        /// 获得所有收藏项，包括收藏与非收藏。 
        /// </summary>
        public async Task<IList<Favorite>> GetFavoriteItemsAsync()
        {
            var rootChildren = await _graphClient.Me.Drive.Root.Children
              .Request().GetAsync();

            if (!rootChildren.Any(p => p.Name == "TabbedTemplate.zip"))
            {
                return new List<Favorite>();
            }

            var fileStream = await _graphClient.Me.Drive.Root
                .ItemWithPath("/TabbedTemplate.zip").Content.Request().GetAsync();

            ZipInputStream zipStream = new ZipInputStream(fileStream);
            ZipEntry zipEntry = zipStream.GetNextEntry();

            if (zipEntry == null)
            {
                return new List<Favorite>();
            }

            byte[] buffer = new byte[1024];
            var jsonStream = new MemoryStream();
            StreamUtils.Copy(zipStream, jsonStream, buffer);
            zipStream.Close();
            fileStream.Close();

            jsonStream.Position = 0;
            var jsonReader = new StreamReader(jsonStream);
            var favoriteList =
                JsonConvert.DeserializeObject<IList<Favorite>>(
                    await jsonReader.ReadToEndAsync());
            jsonReader.Close();
            jsonStream.Close();

            return favoriteList?.ToList() ?? new List<Favorite>();
        }

        /// <summary>
        /// 保存所有收藏项，包括收藏与非收藏。
        /// </summary>
        /// <param name="favoriteList">所有收藏项，包括收藏与非收藏。</param>
        public async Task<ServiceResult> SaveFavoriteItemsAsync(
            IList<Favorite> favoriteList)
        {
            Status = "正在压缩远程收藏项";
           
            var json = JsonConvert.SerializeObject(favoriteList);
            
            MemoryStream fileStream = new MemoryStream();
            ZipOutputStream zipStream = new ZipOutputStream(fileStream);
            zipStream.SetLevel(3);

            ZipEntry newEntry = new ZipEntry("TabbedTemplate.json");
            newEntry.DateTime = DateTime.Now;
            zipStream.PutNextEntry(newEntry);

            var jsonStream = new MemoryStream(Encoding.UTF8.GetBytes(json));
            StreamUtils.Copy(jsonStream, zipStream, new byte[1024]);
            jsonStream.Close();
            zipStream.CloseEntry();

            zipStream.IsStreamOwner = false;
            zipStream.Close();

            Status = "正在上传远程收藏项";
            fileStream.Position = 0;
            try
            {
                await _graphClient.Me.Drive.Root
                    .ItemWithPath("/TabbedTemplate.zip").Content.Request()
                    .PutAsync<DriveItem>(fileStream);
            }
            catch (ServiceException e)
            {
                _alertService.ShowAlert(ErrorMessages.HTTP_CLIENT_ERROR_TITLE,
                    ErrorMessages.HttpClientErrorMessage(Server, e.Message),
                    ErrorMessages.HTTP_CLIENT_ERROR_BUTTON);
                return new ServiceResult
                {
                    Status = ServiceResultStatus.Exception,
                    Message = e.Message
                };
            }
            finally
            {
                fileStream.Close();
            }

            return new ServiceResult { Status = ServiceResultStatus.Ok };
        }
        
        /// <summary>
        /// 将收藏的店铺存进StoreStorage。
        /// </summary>
        /// <param name="favoriteList">所有收藏项，包括收藏与非收藏。</param>
        public async Task<ServiceResult> SaveStoresAsync(IList<Favorite> favoriteList)
        {
            Status = "正在压缩远程收藏项";
            var stores = new List<Store>();
            stores.AddRange(await Task.WhenAll(
                favoriteList.Select(async p =>
                    await _storeStorage.GetStoreByStoreUidAsync(p.StoreUid))));
            var json = JsonConvert.SerializeObject(stores);

            MemoryStream fileStream = new MemoryStream();
            ZipOutputStream zipStream = new ZipOutputStream(fileStream);
            zipStream.SetLevel(3);

            ZipEntry newEntry = new ZipEntry("TabbedTemplateStoreStorage.json");
            newEntry.DateTime = DateTime.Now;
            zipStream.PutNextEntry(newEntry);

            var jsonStream = new MemoryStream(Encoding.UTF8.GetBytes(json));
            StreamUtils.Copy(jsonStream, zipStream, new byte[1024]);
            jsonStream.Close();
            zipStream.CloseEntry();

            zipStream.IsStreamOwner = false;
            zipStream.Close();

            Status = "正在上传远程收藏项";
            fileStream.Position = 0;
            try
            {
                await _graphClient.Me.Drive.Root
                    .ItemWithPath("/TabbedTemplateStoreStorage.zip").Content.Request()
                    .PutAsync<DriveItem>(fileStream);
            }
            catch (ServiceException e)
            {
                _alertService.ShowAlert(ErrorMessages.HTTP_CLIENT_ERROR_TITLE,
                    ErrorMessages.HttpClientErrorMessage(Server, e.Message),
                    ErrorMessages.HTTP_CLIENT_ERROR_BUTTON);
                return new ServiceResult
                {
                    Status = ServiceResultStatus.Exception,
                    Message = e.Message
                };
            }
            finally
            {
                fileStream.Close();
            }

            return new ServiceResult { Status = ServiceResultStatus.Ok };
        }
        /// <summary>
        /// 是否已登录。
        /// </summary>
        public async Task<bool> IsSignedInAsync()
        {
            Status = "正在检查OneDrive登录状态";

            string accessToken = string.Empty;

            try
            {
                var accounts = await _pca.GetAccountsAsync();
                if (accounts.Any())
                {
                    var silentAuthResult = await _pca
                        .AcquireTokenSilent(_scopes, accounts.FirstOrDefault())
                        .ExecuteAsync();
                    accessToken = silentAuthResult.AccessToken;
                }
            }
            catch (MsalUiRequiredException)
            {
                return false;
            }

            return !string.IsNullOrEmpty(accessToken);
        }

        /// <summary>
        /// 登录。
        /// </summary>
        public async Task<bool> SignInAsync()
        {
            Status = "正在登录OneDrive";

            try
            {
                var interactiveRequest = _pca.AcquireTokenInteractive(_scopes);

                if (App.AuthUIParent != null)
                {
                    interactiveRequest = interactiveRequest
                        .WithParentActivityOrWindow(App.AuthUIParent);
                }

                await interactiveRequest.ExecuteAsync();
            }
            catch (MsalClientException e)
            {
                return false;
            }

            return true;
        }

        /// <summary>
        /// 注销。
        /// </summary>
        public async Task SignOutAsync()
        {
            Status = "正在退出OneDrive登录";

            var accounts = await _pca.GetAccountsAsync();
            while (accounts.Any())
            {
                await _pca.RemoveAsync(accounts.First());
                accounts = await _pca.GetAccountsAsync();
            }
        }

        /******** 公开方法 ********/

        /// <summary>
        /// 远程收藏存储。
        /// </summary>
        /// <param name="alertService">警告服务。</param>


        public OneDriveFavoriteStorage(IAlertService alertService,IStoreStorage storeStorage)
        {
            this._alertService = alertService;
            this._storeStorage = storeStorage;

            var builder = PublicClientApplicationBuilder
                .Create(OneDriveOAuthSettings.ApplicationId)
                .WithRedirectUri(OneDriveOAuthSettings.RedirectUri);


            if (!string.IsNullOrEmpty(App.iOSKeychainSecurityGroup))
            {
                builder =
                    builder.WithIosKeychainSecurityGroup(
                        App.iOSKeychainSecurityGroup);
            }

            _pca = builder.Build();

            _graphClient = new GraphServiceClient(
                new DelegateAuthenticationProvider(async (requestMessage) => {
                    var accounts = await _pca.GetAccountsAsync();

                    var result = await _pca
                        .AcquireTokenSilent(_scopes, accounts.FirstOrDefault())
                        .ExecuteAsync();

                    requestMessage.Headers.Authorization =
                        new AuthenticationHeaderValue("Bearer",
                            result.AccessToken);
                }));
        }

        /******** 私有方法 ********/
    }
}
